"""
Models for the TGAM Blogs.
"""
__author__ = 'Jack Hsu'

from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
import datetime
from django.core.cache import cache

def _clear_post_cache(instance):
    '''
    Resets cache for the post and all relevant objects.
    '''
    cache.delete('Blog:%s_tags' % instance.blog.slug)
    cache.delete('Blog:%s_posts' % instance.blog.slug)
    cache.delete('Blog:%s_latest_posts' % instance.blog.slug)
    cache.delete('latest_posts')
    cache.delete('Post:%s' % instance.slug)
    
    pub_date = instance.pub_date
    
    if not pub_date == None:
        [year, month, day] = [pub_date.year, pub_date.month, pub_date.day]
        cache.delete('Blog:%s_archive:%s_%s_%s' % (instance.blog.slug, year, month, day))
        cache.delete('Blog:%s_archive:%s_%s' % (instance.blog.slug, year, month))
        cache.delete('Blog:%s_archive:%s' % (instance.blog.slug, year))

            
    for tag in instance.get_tags():
        cache.delete('%s__tag__%s' % (instance.blog.slug, tag.name))

def _clear_blog_cache(instance):
    '''
    Resets cache for the blog and all relevant objects.
    '''
    cache.delete('Blog:%s' % instance.slug)
    
def _clear_link_cache(instance):
    '''
    Resets cache for the blogroll.
    '''
    cache.delete('Blog:%s_blogroll' % instance.blog.slug)
    
def _clear_comment_cache(instance):
    '''
    Resets cache for the comment.
    '''
    cache.delete('Post:%s_comments' % instance.post.slug)
    cache.delete('Blog:%s_latest_comments' % instance.blog.slug)
    cache.delete('latest_comments')

class Blog(models.Model):
    '''
    >>> b=Blog(slug='foo',name='Foo')
    >>> b.save()
    >>> b
    <Blog: Foo>
    '''
    def __unicode__(self):
        return self.name
    
    class Admin:
        list_display = ('name', 'description')
        
    def save(self):
        super(Blog, self).save()
        _clear_blog_cache(self)
        
    def delete(self):
        super(Blog, self).delete()
        _clear_blog_cache(self)
    
    def get_posts(self, drafts=False, order_by='-pub_date'):
        '''
        Returns posts for this Blog. The results are limited by the at_most member.
        
        Parameters:
        start -- starting index for result set
        drafts -- True to return only drafts, False to return only published posts
        order_by -- expression specifying the order of the result set
        
        >>> from datetime import datetime
        >>> b = Blog(slug='foo-bar', name='Foo Bar')
        >>> b.save()
        >>> p1=Post(slug='draft',title='Draft',blog=b, mod_date=datetime(2008, 3, 18))
        >>> p1.save()
        >>> p2=Post(slug='draft-2',title='Draft 2',blog=b, mod_date=datetime(2008, 3, 17))
        >>> p2.save()
        >>> p3=Post(slug='published',title='Published',blog=b,pub_date=datetime(2008, 3, 17))
        >>> p3.save()
        >>> p4=Post(slug='published-2',title='Published 2',blog=b,pub_date=datetime(2008, 3, 18))
        >>> p4.save()
        >>> b=Blog.objects.get(slug='foo-bar')
        >>> b.get_posts()
        [<Post: Published 2>, <Post: Published>]
        >>> b.get_posts(drafts=True)
        [<Post: Draft>, <Post: Draft 2>]
        '''
        
        return Post.objects.filter(blog=self.slug, pub_date__isnull = drafts).order_by(order_by)
    
    def get_post_count(self, drafts=False):
        '''
        Gets the total number of posts for this Blog.
        
        >>> from datetime import datetime
        >>> b = Blog(slug='foo-bar', name='Foo Bar')
        >>> b.save()
        >>> p1=Post(slug='draft',title='Draft',blog=b)
        >>> p1.save()
        >>> p2=Post(slug='published',title='Published',blog=b,pub_date=datetime.now())
        >>> p2.save()
        >>> b=Blog.objects.get(slug='foo-bar')
        >>> int(b.get_post_count())
        1
        >>> int(b.get_post_count(drafts=True))
        1
        '''
        return Post.objects.filter(blog=self.slug, pub_date__isnull=drafts).count()
    
    def get_absolute_url(self):
        return "/blogs/%s/" % self.slug
    
    name = models.CharField(max_length=200)
    slug = models.SlugField(prepopulate_from=('name',), primary_key=True, help_text='Unique identifier for the blog.')
    description = models.TextField(help_text='Short description of this blog.')
    users = models.ManyToManyField(User, help_text='Writers for this blog.')
    at_most = models.IntegerField('Show at most', default=5, help_text='Number of posts to show for this blog.')
    date_format = models.CharField('date format', max_length=30, default='F j, Y', help_text='Date format for this blog.')
    time_format = models.CharField('time format', max_length=30, default='g:i A', help_text='Time format for this blog.')


class Tag(models.Model):
    def __unicode__(self):
        return self.name  
        
    def _get_ref_count(self):
        return Post.objects.filter(blog=self.blog, pub_date__isnull=False, tags__name__exact=self.name).count()
    
    def __cmp__(self, other):
        return cmp(self.ref_count, other.ref_count)
    
    class Admin:
        list_display = ('name', 'blog',)
        list_filter = ('blog',)
      
    blog = models.ForeignKey(Blog)
    name = models.CharField(max_length=200, core=True)
    ref_count = property(_get_ref_count)


class Post(models.Model):
    '''
        This is a blog post object.
    '''

    def __unicode__(self):
        return self.title
    
    class Admin:
        fields = (
            ('Post information', {'fields': ('blog', 'title', 'slug', 'pub_date', 'byline', 'body', 'tags', 'comments_enabled', 'mod_date', 'mod_user')}),
        )
        list_display = ('title', 'mod_date', 'pub_date', 'blog', 'byline',)
        list_filter = ('blog', 'pub_date',)
        search_fields = ['title']
        date_hierarchy = 'pub_date'
        
    def save(self):
        super(Post, self).save()
        _clear_post_cache(self)
        
    def delete(self):
        _clear_post_cache(self)
        super(Post, self).delete()
    
    def get_absolute_url(self):
        '''
        Returns the absolute URL of this Post. (prefixed by a "/")
        Posts are in the format /blogs/Y/m/d/slug/ e.g. /blogs/front-end/2008/03/17/test-post/.
        
        >>> from datetime import datetime
        >>> pub_date = datetime(2008, 3, 17)
        >>> b=Blog(slug='foo-bar')
        >>> p=Post(blog=b, pub_date=pub_date, slug='test')
        >>> p.get_absolute_url()
        /blogs/foo-bar/2008/03/17/test/
        '''
        return "/blogs/%s/%s/%s/" % (self.blog.slug, self.pub_date.strftime("%Y/%m/%d").lower(), self.slug)
    
    def get_author(self):
        '''
        Returns the author of this Post
        
        >>> from django.contrib.auth.models import User

        >>> u=User(username='jhsu', first_name = 'Jack', last_name = 'Hsu', email='jhsu@globeandmail.ca')
        >>> u.save()
        >>> p=Post(byline='jhsu', slug='draft',title='Draft')
        >>> author = p.get_author()
        >>> author['name']
        u'Jack Hsu'
        >>> author['email']
        u'jhsu@globeandmail.ca'
        '''
        user = User.objects.filter(username__exact=self.byline)
        
        if user:
            name = user[0].first_name + " " + user[0].last_name
            email = user[0].email
        else:
            name = self.byline
            email = ''
        
        if not name.strip() == '':
            return { 'name': name.strip(), 'email': email }
            
        # Cannot find user, return byline
        return { 'name': self.byline, 'email': '' }
        
    def get_tags(self):
        '''
        Get all tags associated with this Post
        
        >>> b=Blog(slug='foo')
        >>> b.save()
        >>> t1=Tag(name='hello', blog=b)
        >>> t2=Tag(name='world', blog=b)
        >>> t1.save()
        >>> t2.save()
        >>> p=Post(blog=b, slug='hello-world')
        >>> p.save()
        >>> p.tags.add(t1)
        >>> p.tags.add(t2)
        >>> p.get_tags()
        [<Tag: hello>, <Tag: world>]
        
        '''
        return self.tags.all()
        
    def get_comments(self):
        '''
        Get all comments for this Post.
        
        >>> from datetime import datetime
        >>> b=Blog(slug='foo')
        >>> b.save()
        >>> p=Post(blog=b, slug='hello-world')
        >>> p.save()
        >>> c=Comment(blog=b, post=p, name='Jack', email='jhsu@globeandmail.ca', date=datetime.now(), ip='10.100.25.41', message='Cool!')
        >>> c.save()
        >>> p.get_comments()
        [<Comment: Cool!>]
        '''
        return Comment.objects.filter(blog=self.blog, post=self)
        
    def is_published(self):
        '''
        Indicates whether this Post has been published or not.
        
        >>> from datetime import datetime
        >>> published=Post(pub_date=datetime.now())
        >>> published.is_published()
        True
        >>> draft=Post()
        >>> draft.is_published()
        False
        '''
        return not self.pub_date == None
    
    blog = models.ForeignKey(Blog)
    title = models.CharField(max_length=200)
    slug = models.SlugField(prepopulate_from=('title',), unique=True)
    mod_date = models.DateTimeField('date modified', auto_now=True)
    pub_date = models.DateTimeField('date published', null=True)
    byline =  models.CharField(max_length=200)
    mod_user =  models.CharField(max_length=200)
    body = models.TextField()
    tags = models.ManyToManyField(Tag, help_text='Tag for this post.')
    comments_enabled = models.BooleanField('comments enabled', default=True)


class Comment(models.Model):
    def __unicode__(self):
        return self.message
        
    class Admin:
        list_display = ('blog', 'post', 'name', 'ip', 'message', 'date')
        list_filter = ('blog', 'post',)
        search_fields = ['subject']
        date_hierarchy = 'date'
        
    def save(self):
        super(Comment, self).save()
        _clear_comment_cache(self)
        
    def delete(self):
        super(Comment, self).delete()
        _clear_comment_cache(self)
        
    blog = models.ForeignKey(Blog)
    post = models.ForeignKey(Post)
    name = models.CharField(max_length=100)
    email = models.EmailField('e-mail')
    website = models.CharField(max_length=255, null=True)
    ip = models.IPAddressField('IP address')
    date = models.DateTimeField('date posted', auto_now_add=True)
    message = models.TextField()


class Banned(models.Model):
    '''
    Allows admins to ban specific IPs from commenting on the blog.
    Goes by per-blog basis so each blog can maintain their own list of banned IPs.
    '''
    def __unicode__(self):
        return self.ip
    
    class Admin:
        pass
        
    blog = models.ForeignKey(Blog)
    ip = models.IPAddressField('banned IP', primary_key=True)


class Link(models.Model):
    def __unicode__(self):
        return self.url
    
    class Admin:
        pass
        
    def save(self):
        super(Link, self).save()
        _clear_link_cache(self)
        
    def delete(self):
        super(Link, self).delete()
        _clear_link_cache(self)
    
    blog = models.ForeignKey(Blog)
    name = models.CharField(max_length=100)
    url = models.URLField()
    tooltip = models.CharField(max_length=100, null=True, help_text='Tooltip for this link. Rendered as title HTML attribute.')
    active = models.BooleanField(default=True)
    
    
class Image(models.Model):
    def __unicode__(self):
        return self.title
        
    class Admin:
        pass
        
    blog = models.ForeignKey(Blog)
    title = models.CharField(max_length=100, help_text='Appears as the title HTML attribute')
    file = models.ImageField(blank=True, upload_to='uploads/blogs')
    date = models.DateTimeField('date added', auto_now_add=True)
